其他
原创 | SpringWeb常见鉴权措施与垂直越权检测
越权测试中的痛点/难点
越权漏洞是日常开发中比较常见的一个缺陷。要进行越权检测,一般需要明确定义和管理系统中的权限。这可能包括用户角色、资源和操作的细粒度权限控制。维护这些权限定义并确保它们与实际业务操作一致本身就是是一项复杂的任务。
以垂直越权为例,一般测试时,首先会获取到高权限用户模块的业务数据包,然后在BurpSuite将其鉴权凭证(一般是cookie)替换成低权限用户的,也可以构造好高权限的业务数据包,然后在浏览器登陆低权限用户,通过访问相关的数据包,若业务依旧可以成功访问,则存在垂直越权缺陷。但是实际测试过程中可能会遇到一系列的问题:
在测试过程中,了解应用程序的请求接口和其参数是至关重要的。这包括了解哪些参数控制了访问权限、哪些参数可以被滥用,以及如何构造恶意请求。但是在实际测试时,经常是通过js代码审计发现潜在的垂直越权接口,请求参数信息可能不完整或模糊导致没办法进步一测试(这些信息可能不容易获取,因为无法获取应用程序的源代码,也无法直接查看其接口文档)。 一些越权漏洞可能导致误删除/修改/更新操作,在没有测试环境的情况下数据恢复比较困难。 在黑盒扫描中,需要获取足够的应用程序流量数据,以便进行分析和检测。然而,流量可能受到加密、访问权限和网络配置的限制。
除此以外,还有还多其他的因素,下面先看看SpringWeb中常见鉴权措施与解析顺序,简单探索下能不能在一定程度上解决上述的问题。
SpringWeb中常见鉴权措施
2.1 过滤器Filter
@Bean
public FilterRegistrationBean<AuthFilter> FilterConfig() {
FilterRegistrationBean<AuthFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new AuthFilter());
registrationBean.addUrlPatterns("/admin/*");
return registrationBean;
}
假设/admin存在如下更新用户的接口:
@RequestMapping(value="/user/add",method = RequestMethod.POST)
public ApiResponse<User> addUser (@RequestBody User user){
return new ApiResponse<>(200, "Success", userService.saveOrUpdate(user));
}
当使用GET方式请求该接口时,会返回405 Status
当通过 application/x-www-form-urlencoded;charset=UTF-8方式请求该接口时,会返回415 Status
当通过application/json方式请求,但是传递参数为null时,会返回400 Status:
2.2 拦截器Interceptor( preHandle )
preHandle:Controller方法执行之前执行preHandle(),返回值是一个boolean,表示是否拦截或放行,返回true为放行,即调用控制器方法;返回false表示拦截,即不调用控制器方法 postHandle:Controller方法执行之后执行postHandle() afterComplation:处理完视图和模型数据,渲染视图完毕之后执行afterComplation()
在鉴权场景中,拦截器常常用于在请求处理前执行,也就是一般会调用preHandle,后续仅讨论preHandle的情况 。
public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 在请求处理前执行,可进行鉴权逻辑
// 获取当前用户信息
String currentUser = getCurrentUser(request);
// 进行垂直越权检测
if (!hasPermission(currentUser)) {
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
return false; // 拒绝访问
}
return true; // 允许访问
}
private String getCurrentUser(HttpServletRequest request) {
// 实现获取当前用户的逻辑
}
private boolean hasPermission(String currentUser, String resourceId) {
// 实现垂直越权检测逻辑
}
}
在配置类添加@Configuration注解,通过重写addInterceptors方法,添加拦截器,并配置匹配路径,AuthInterceptor对/admin/目录下的所有资源都生效,也就是说这是一个垂直鉴权的措施:
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册拦截器
registry.addInterceptor(new AuthInterceptor(environment)).addPathPatterns("/admin/**").excludePathPatterns("/test/**");
}
这是因为在RequestMappingHandlerMapping的getHandler方法中通过getHandlerInternal获取handler时,会对请求匹配对应的handler,其中一处就是请求Method的匹配,若资源不存在的话,也没必要进一步处理了:
2.3 切面
@Aspect :指定切面类(可以通过检索@Aspect快速定位切面类) @Pointcut :定义了相应的 Advice(具体要做的操作)触发的地方。一般是通过通配符、正则表达式等方式。
@Pointcut("@annotation(Auth)")
public void requirePermissionAuth() {
通知(Advice)类型:定义要执行的方法,安全中一般是鉴权 @Before: 在目标方法执行之前,执行注解标记的内容 @After: 在目标方法执行之后,执行注解标记的内容 @AfterReturning: 在目标方法返回后,执行注解标记的内容 @AfterThrowing: 在目标方法抛出异常后,执行注解标记的内容 @Around: 在目标方法执行前后,分别执行对应的内容
2.4 在 Service 层实施鉴权
@Service
public class UserService {
public boolean hasPermissionToDeleteUser(User currentUser, User userToDelete) {
// 自定义鉴权逻辑,检查是否允许当前用户删除指定用户
if (currentUser.isAdmin()) {
return true; // 管理员可以删除任何用户
} else {
return currentUser.getId().equals(userToDelete.getId()); // 用户只能删除自己
}
}
public User deleteUser(User currentUser, User userToDelete) {
if (hasPermissionToDeleteUser(currentUser, userToDelete)) {
// 执行删除用户的业务逻辑
return userToDelete;
} else {
throw new SecurityException("没有权限删除用户");
}
}
// 其他方法...
}
鉴权措施的执行顺序
简单的垂直越权监测
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests().requestMatchers("/admin/**","/manage/**").hasRole("ADMIN").anyRequest().permitAll();
return http.build();
}
}
往期推荐